defmodule SockWorkstation.FrameHelperGen do
  use GenServer

  alias SockWorkstation.{SockCoopClient, FrameHelperGen, CodecNifGen}

  # 默认发送间隔(ms)
  @default_interval 58
  # 默认背压阈值 - 当帧数达到此值时自动开始推送
  @default_backpressure_threshold 6
  require Logger
  # PCM分块大小(字节)
  @chunk_size 1920

  @name __MODULE__

  defstruct frames: [],
            interval: @default_interval,
            timer_ref: nil,
            receiver_pid: nil,
            self_pid: nil,
            session_id: nil,
            coop_pid: nil,
            backpressure_threshold: @default_backpressure_threshold,
            auto_push?: true,
            # 新增PCM缓冲区
            pcm_buffer: <<>>

  # 客户端 API

  def start_link(session_id) do
    # interval = Keyword.get(opts, :interval, @default_interval)
    GenServer.start_link(@name, session_id, name: via_tuple(session_id, "frame_helper"))
    # GenServer.start_link(@name, {frames, interval}, name: @name)
  end

  @doc """
  设置接收者PID并开始推送
  """
  def start_push(session_id) do
    GenServer.call(via_tuple(session_id, "frame_helper"), :start_push)
  end

  @doc """
  停止推送
  """

  def stop_push(session_id) do
    GenServer.call(via_tuple(session_id, "frame_helper"), :stop_push)
  end

  def pause_push(session_id) do
    GenServer.call(via_tuple(session_id, "frame_helper"), :pause_push)
  end

  def push_frames(session_id, frames) do
    stop_push(session_id)
    queue_frames(session_id, frames)
    start_push(session_id)
  end

  @doc """
  设置推送间隔(毫秒)
  """
  def set_interval(session_id, interval_ms) when is_integer(interval_ms) and interval_ms > 0 do
    GenServer.call(via_tuple(session_id, "frame_helper"), {:set_interval, interval_ms})
  end

  @doc """
  设置背压阈值 - 当帧数达到此值时自动开始推送
  """
  def set_backpressure_threshold(session_id, threshold)
      when is_integer(threshold) and threshold > 0 do
    GenServer.call(
      via_tuple(session_id, "frame_helper"),
      {:set_backpressure_threshold, threshold}
    )
  end

  @doc """
  启用或禁用自动推送(基于背压)
  """
  def set_auto_push(session_id, enabled) when is_boolean(enabled) do
    GenServer.call(via_tuple(session_id, "frame_helper"), {:set_auto_push, enabled})
  end

  @doc """
  获取当前状态
  """
  def get_state(session_id) do
    GenServer.call(via_tuple(session_id, "frame_helper"), :get_state)
  end

  def fetch_coop_pid(session_id, coop_pid) do
    GenServer.cast(via_tuple(session_id, "frame_helper"), {:fetch_coop_pid, coop_pid})
  end

  # def rm_coop_pid(session_id) do
  #   GenServer.cast(via_tuple(session_id, "frame_helper"), :rm_coop_pid)
  # end
  def fetch_pcmbin(session_id, pcm_data) do
    GenServer.cast(via_tuple(session_id, "frame_helper"), {:fetch_pcmbin, pcm_data})
  end

  def enqueue_pcmbin(session_id) do
    GenServer.cast(via_tuple(session_id, "frame_helper"), :enqueue_pcmbin)
  end

  def queue_frames(session_id, frames) do
    GenServer.cast(via_tuple(session_id, "frame_helper"), {:queue_frames, frames})
  end

  def append_frame(session_id, frame) do
    GenServer.cast(via_tuple(session_id, "frame_helper"), {:append_frame, frame})
  end

  # 服务器回调

  @impl true
  def init(session_id) do
    # backpressure_threshold = Keyword.get(opts, :backpressure_threshold, @default_backpressure_threshold)
    # auto_push = Keyword.get(opts, :auto_push, false)

    state = %FrameHelperGen{
      session_id: session_id,
      self_pid: self()
      # backpressure_threshold: @backpressure_threshold,
      # auto_push: auto_push
    }

    {:ok, state}
  end

  @impl true
  def handle_call(:get_state, _from, state) do
    {:reply, state, state}
  end

  def handle_call(:start_push, _from, state) do
    case state.receiver_pid do
      nil ->
        new_state = schedule_next_frame(%{state | receiver_pid: state.coop_pid})
        {:reply, :ok, new_state}

      _ ->
        {:reply, {:error, :already_pushing}, state}
    end
  end

  def handle_call(:pause_push, _from, state) do
    state =
      state
      |> cancel_timer()
      |> Map.put(:receiver_pid, nil)

    {:reply, :ok, state}
  end

  def handle_call(:stop_push, _from, state) do
    state =
      state
      |> cancel_timer()
      |> Map.put(:receiver_pid, nil)
      |> Map.put(:frames, [])

    {:reply, :ok, state}
  end

  def handle_call({:set_interval, interval}, _from, state) do
    new_state =
      state
      |> cancel_timer()
      |> Map.put(:interval, interval)

    # 如果正在推送，则用新间隔重新安排
    new_state =
      if state.receiver_pid do
        schedule_next_frame(new_state)
      else
        new_state
      end

    {:reply, :ok, new_state}
  end

  def handle_call({:set_backpressure_threshold, threshold}, _from, state) do
    {:reply, :ok, %{state | backpressure_threshold: threshold}}
  end

  def handle_call({:set_auto_push, enabled}, _from, state) do
    {:reply, :ok, %{state | auto_push?: enabled}}
  end

  @impl true

  def handle_cast({:queue_frames, frames}, state) do
    # new_state = %FrameHelperGen{state | frames: state.frames ++ frames}
    new_frames = state.frames ++ frames
    new_state = %{state | frames: new_frames}

    # 检查是否需要自动开始推送
    new_state = maybe_auto_start(new_state, length(new_frames))

    {:noreply, new_state}
  end

  def handle_cast({:append_frame, frame}, state) do
    IO.puts("FrameHelper Received frame length: #{inspect(byte_size(frame))}")
    # new_state = %FrameHelperGen{state | frames: state.frames ++ [frame]}
    # new_state = %FrameHelperGen{
    #   state
    #   | frames: [frame | Enum.reverse(state.frames)] |> Enum.reverse()
    # }
    new_frames = [frame | Enum.reverse(state.frames)] |> Enum.reverse()
    new_state = %{state | frames: new_frames}
    # IO.puts("FrameHelper Received frame length: #{inspect(length(new_frames))}")
    # 检查是否需要自动开始推送
    new_state = maybe_auto_start(new_state, length(new_frames))

    {:noreply, new_state}
  end

  def handle_cast({:fetch_coop_pid, coop_pid}, state) when coop_pid == state.coop_pid do
    {:noreply, state}
  end

  def handle_cast({:fetch_coop_pid, coop_pid}, state) do
    {:noreply, %FrameHelperGen{state | coop_pid: coop_pid}}
  end

  def handle_cast({:fetch_pcmbin, pcm_data}, state) do
    # IO.puts("FrameHelper Received pcm frame length: #{inspect(byte_size(pcm_data))}")
    # send(state.self_pid, :enqueue_pcmbin)
    pcm_buffer = <<state.pcm_buffer::binary, pcm_data::binary>>
    {:noreply, %{state | pcm_buffer: pcm_buffer}}
  end

  def handle_cast(:enqueue_pcmbin, state) when is_binary(state.pcm_buffer) do
    # IO.puts("FrameHelper Received pcm frame length: #{inspect(byte_size(state.pcm_buffer))}")
    case split_data(state.pcm_buffer, @chunk_size) do
      {pcm_chunk, <<>>} ->
        {:ok, opus_data} = CodecNifGen.encode_pcm(state.session_id, pcm_chunk)
        append_frame(state.session_id, opus_data)
        # enqueue_pcmbin(state.session_id)
        {:noreply, %{state | pcm_buffer: <<>>}}

      {pcm_chunk, remaining_buffers} ->
        {:ok, opus_data} = CodecNifGen.encode_pcm(state.session_id, pcm_chunk)
        append_frame(state.session_id, opus_data)
        enqueue_pcmbin(state.session_id)
        {:noreply, %{state | pcm_buffer: remaining_buffers}}
    end
  end

  # def handle_cast(:rm_coop_pid, state) do
  #   {:noreply, %FrameHelperGen{state | coop_pid: nil}}
  # end

  @impl true
  def handle_info(:push_next, %FrameHelperGen{frames: []} = state) do
    state =
      state
      |> cancel_timer()
      |> Map.put(:receiver_pid, nil)

    {:noreply, state}
  end

  def handle_info(:push_next, state) do
    case send_frame(state) do
      {:ok, new_state} ->
        {:noreply, schedule_next_frame(new_state)}

      {:error, reason, new_state} ->
        {:stop, reason, new_state}
    end
  end

  # def terminate(_reason, state) do
  # end
  # 私有函数

  defp send_frame(%FrameHelperGen{frames: [frame | rest]} = state) when is_pid(state.coop_pid) do
    case SockCoopClient.send_binary(state.coop_pid, frame) do
      :ok ->
        # stats = %FrameHelperGen{
        #   sent: state.stats.sent + 1,
        #   remaining: length(rest),
        #   last_sent_at: DateTime.utc_now()
        # }

        {:ok, %FrameHelperGen{state | frames: rest}}

      {:error, reason} ->
        {:error, reason, state}
    end
  end

  defp send_frame(state) do
    {:ok, state}
  end

  defp schedule_next_frame(state) do
    timer_ref = Process.send_after(self(), :push_next, state.interval)
    %{state | timer_ref: timer_ref}
  end

  defp cancel_timer(%{timer_ref: nil} = state), do: state

  defp cancel_timer(state) do
    Process.cancel_timer(state.timer_ref)
    %{state | timer_ref: nil}
  end

  # 检查是否需要自动开始推送
  defp maybe_auto_start(state, frame_count) do
    if state.auto_push? && frame_count >= state.backpressure_threshold &&
         is_nil(state.receiver_pid) do
      schedule_next_frame(%{state | receiver_pid: state.coop_pid})
    else
      state
    end
  end

  defp split_data(data, size) when byte_size(data) > size do
    <<chunk::binary-size(size), remaining::binary>> = data
    {chunk, remaining}
    # padded_data = data <> :binary.copy(<<0>>, @chunk_size - byte_size(data))
    # string_data = :binary.bin_to_list(data)
    # IO.puts(byte_size(padded_data))
    # {padded_data, <<>>}
  end

  defp split_data(data, size) do
    padded_data = data <> :binary.copy(<<0>>, size - byte_size(data))
    # string_data = :binary.bin_to_list(data)
    # IO.puts(byte_size(padded_data))
    {padded_data, <<>>}
    # <<chunk::binary-size(size), remaining::binary>> = data
    # {chunk, remaining}
  end

  defp via_tuple(session_id, type) do
    {:via, Registry, {SockWorkstation.SessionRegistry, {session_id, type}}}
  end
end

# def handle_cast(:start_transpcm, state) do
#   case split_data(state.bin_buffer, @chunk_size) do
#     {pcm_chunk, <<>>} ->
#       {:ok, opus_data} = CodecNifGen.encode_pcm(session_id, pcm_chunk)
#       append_frame(state.sesson_id, opus_data)
#       {:noreply, %{state | bin_buffer: <<>>}}

#     {pcm_chunk, remaining_buffers} ->
#       {:ok, opus_data} = CodecNifGen.encode_pcm(session_id, pcm_chunk)
#       append_frame(state.sesson_id, opus_data)
#       {:noreply, %{state | bin_buffer: remaining_buffers}}
#   end
# end

# def handle_cast({:add_pcm, pcm_data}, state) when is_binary(pcm_data) do
#   # 计算完整块数
#   chunk_count = div(byte_size(pcm_data), @chunk_size)

#   # 处理完整块（丢弃不足1920的部分）
#   frames =
#     if chunk_count > 0 do
#       # 只取完整块
#       <<chunks::binary-size(chunk_count * @chunk_size), _rest::binary>> = pcm_data

#       chunks
#       |> String.codepoints()
#       |> Enum.chunk_every(@chunk_size)
#       |> Enum.map(&(&1 |> Enum.join() |> encode_pcm(state.session_id)))
#       |> Enum.filter(fn
#         {:ok, _frame} -> true
#         {:error, _reason} -> false
#       end)
#       |> Enum.map(fn {:ok, frame} -> frame end)
#     else
#       # 不足1920字节，全部丢弃
#       []
#     end

#   # 更新状态
#   new_state =
#     state
#     |> Map.update!(:frames, &(&1 ++ frames))
#     |> maybe_auto_start()

#   {:noreply, new_state}
# end

# def handle_cast({:add_pcm, _invalid_data}, state) do
#   Logger.error("Invalid PCM data received, expected binary")
#   {:noreply, state}
# end

# defp maybe_auto_start(%{frames: frames} = state) do
#   if state.auto_push? && length(frames) >= state.backpressure_threshold &&
#        is_nil(state.receiver_pid) do
#     schedule_next_frame(%{state | receiver_pid: state.coop_pid})
#   else
#     state
#   end
# end

# defp encode_pcm_frames(chunks, session_id) do
#   Enum.map(chunks, &encode_pcm(&1, session_id))
# end

# defp chunk_pcm_data(data) do
#   data
#   |> String.codepoints()
#   |> Enum.chunk_every(@chunk_size)
#   |> Enum.map(&Enum.join/1)
# end

# defp filter_valid_frames(encoded_results) do
#   encoded_results
#   |> Enum.filter(&match?({:ok, _}, &1))
#   |> Enum.map(fn {:ok, frame} -> frame end)
# end

# defp process_pcm_data(data, session_id) do
#   chunk_size = @chunk_size
#   data_size = byte_size(data)

#   if data_size >= chunk_size do
#     # 计算完整块数
#     chunk_count = div(data_size, chunk_size)
#     # 安全分割数据
#     <<chunks::binary-size(chunk_count * chunk_size), rest::binary>> = data

#     # 处理分块
#     frames =
#       chunks
#       |> chunk_pcm_data()
#       |> encode_pcm_frames(session_id)
#       |> filter_valid_frames()

#     {frames, rest}
#   else
#     # 不足一个完整块，全部保留在缓冲区
#     {[], data}
#   end
# end

# defp encode_pcm(pcm_chunk, session_id) do
#   case CodecNifGen.encode_pcm(session_id, pcm_chunk) do
#     {:ok, opus_data} ->
#       {:ok, opus_data}

#     {:error, reason} ->
#       Logger.error("Failed to encode PCM chunk: #{inspect(reason)}")
#       {:error, reason}
#   end
# end

# defp split_data(data, size) do
#   padded_data = data <> :binary.copy(<<0>>, size - byte_size(data))
#   # string_data = :binary.bin_to_list(data)
#   # IO.puts(byte_size(padded_data))
#   {padded_data, <<>>}
#   # <<chunk::binary-size(size), remaining::binary>> = data
#   # {chunk, remaining}
# end

# defp split_data(data, size) when byte_size(data) > size do
#   <<chunk::binary-size(size), remaining::binary>> = data
#   {chunk, remaining}
#   # padded_data = data <> :binary.copy(<<0>>, @chunk_size - byte_size(data))
#   # string_data = :binary.bin_to_list(data)
#   # IO.puts(byte_size(padded_data))
#   # {padded_data, <<>>}
# end

# state = %FrameHelperGen{
#   frames: [],
#   interval: @default_interval,
#   receiver_pid: nil,
#   timer_ref: nil,
#   session_id: session_id,
#   coop_pid: nil
#   # stats: %{
#   #   sent: 0,
#   #   remaining: 0,
#   #   last_sent_at: nil
#   # }
# }

# state = %FrameHelperGen{
#   frames: [],
#   interval: @default_interval,
#   receiver_pid: nil,
#   timer_ref: nil,
#   session_id: session_id,
#   coop_pid: nil
#   # stats: %{
#   #   sent: 0,
#   #   remaining: 0,
#   #   last_sent_at: nil
#   # }
# }

#   def handle_cast({:add_pcm, pcm_data}, %{pcm_buffer: buffer} = state) when is_binary(pcm_data) do
#   # 合并新数据和缓冲区
#   combined_data = buffer <> pcm_data

#   # 安全分割数据并处理
#   {frames, remaining} = process_pcm_data(combined_data, state.session_id)

#   # 更新状态
#   new_state =
#     state
#     |> update_frames(frames)
#     |> Map.put(:pcm_buffer, remaining)
#     |> maybe_auto_start()

#   {:noreply, new_state}
# end
# def handle_cast({:add_pcm, _invalid_data}, state) do
#   Logger.error("Invalid PCM data received, expected binary")
#   {:noreply, state}
# end
# def handle_cast({:add_pcm, pcm_data}, state) do
#   # 合并新数据和缓冲区
#   combined_data = state.pcm_buffer <> pcm_data
#   # 计算完整块数
#   chunk_count = div(byte_size(combined_data), @chunk_size)

#   # 处理完整块
#   {frames, remaining_data} =
#     if chunk_count > 0 do
#       # 分割数据
#       <<chunks::binary-size(chunk_count * @chunk_size), rest::binary>> = combined_data

#       # 分块编码，过滤掉错误的结果
#       frames =
#         chunks
#         |> String.codepoints()
#         |> Enum.chunk_every(@chunk_size)
#         |> Enum.map(&(&1 |> Enum.join() |> encode_pcm(state.session_id)))
#         |> Enum.filter(fn
#           {:ok, _frame} -> true
#           {:error, _reason} -> false
#         end)
#         |> Enum.map(fn {:ok, frame} -> frame end)

#       {frames, rest}
#     else
#       []
#     end

#   # 更新状态
#   new_state =
#     state
#     |> Map.update!(:frames, &(&1 ++ frames))
#     |> Map.put(:pcm_buffer, remaining_data)

#   # 检查是否需要自动开始推送
#   new_state = maybe_auto_start(new_state, length(new_state.frames))

#   {:noreply, new_state}
# end

# PCM编码函数，处理错误情况

# def handle_cast({:add_pcm, pcm_data}, state) do
#   # 合并新数据和缓冲区
#   combined_data = state.pcm_buffer <> pcm_data
#   # 计算完整块数
#   chunk_count = div(byte_size(combined_data), @chunk_size)

#   # 处理完整块
#   {frames, remaining_data} =
#     if chunk_count > 0 do
#       # 分割数据
#       <<chunks::binary-size(chunk_count * @chunk_size), rest::binary>> = combined_data
#       # 分块编码
#       frames =
#         chunks
#         |> String.codepoints()
#         |> Enum.chunk_every(@chunk_size)
#         |> Enum.map(&(&1 |> Enum.join() |> encode_pcm(state.session_id)))

#       {frames, rest}
#     else
#       {[], combined_data}
#     end

#   # 更新状态
#   new_state =
#     state
#     |> Map.update!(:frames, &(&1 ++ frames))
#     |> Map.put(:pcm_buffer, remaining_data)

#   # 检查是否需要自动开始推送
#   new_state = maybe_auto_start(new_state, length(new_state.frames))

#   {:noreply, new_state}
# end
